正则表达式
# 正则表达式
参考链接:正则表达式不要背 (opens new window)
- regulation expression
- 计算机能读懂的规则
- 操作字符串 [TOC]
# 一、写法
let re = /a/;
let re = new RegExp('a');
let re = /a/i
let re = new RegExp('a','i');
1
2
3
4
2
3
4
# 1.1 转义字符\
大的都不要~(大写的非~)
转义字符 | 含义 |
---|---|
\s | 空格 space |
\d | 数字 digit |
\w | 字符(字母、数字)word 等价于**[a-zA-Z0-9]** |
\b | 独立部分 boundary |
\S | 非空格 |
\D | 非数字 |
\W | 非字符 |
\B | 非独立部分 |
. | 任意字符,不包含空格符 |
\. | 点 |
| | 或 |
* | 出现 >= 0 |
+ | 出现 >= 1 |
? | 出现 0 | 1 |
^ | 正则开始的位置 |
$ | 正则结束的位置 |
特殊字符 | 正则表达式 | 记忆方式 |
---|---|---|
换行符 | \n | new line |
换页符 | \f | form feed |
回车符 | \r | return |
空白符 | \s | space |
制表符 | \t | tab |
垂直制表符 | \v | vertical tab |
回退符 | [\b] | backspace,之所以使用[]符号是避免和\b重复 |
# 1.2 标志
- 可混合使用。
let re = /^[a-zA-Z]/gim;
1
标志 | 描述 |
---|---|
g | 全局搜索 global |
i | 不区分大小写搜索 ignore |
m | 多行搜索 multiple line |
y | “粘性”搜索,匹配从目标字符串的当前位置开始 |
# 二、正则
# 2.1 匹配字符串
正则 | 成功 | 失败 |
---|---|---|
re.test(str) | true | false |
str.search(re) | 成功的位置 | -1 |
str.match(re) | 成功的数组 | null |
str.replace(re,'new str'/回调函数(匹配成功的字符)) | 替换/fn | 无 |
注意事项
- replace不会修改原字符串(字符串是只读属性)。
- search不支持全局匹配。
let str = 'abcdefg'; str.search(/[d-g]/g); // 3 str.match(/[d-g]/g); // ["d", "e", "f", "g"] str.replace(/[d-g]/g,'o'); // "abcoooo"
1
2
3
4
# 2.2 匹配独立项
- 独立部分:起始位置,结束位置,前面或者后面有一个空格。
- 隐式位置” \b,匹配这样的位置:它的前一个“显式位置”字符和后一个“显式位置”字符不全是 \w。
let str = 'moon';
alert(/\bm/.test(str));//true
alert(/oon\b/.test(str));//true
1
2
3
2
3
# 2.3 匹配子项()
- 一个正则表达式模式使用括号,将导致相应的子匹配被记住。
# 2.3.1 分组操作
- 把正则的整体叫做(母亲)
- 把左边第一个小括号里面的正则,叫做这个第一个子项(母亲的第一个孩子)
- 第二个小括号就是第二个孩子
let str = '2019-2-14';
let re = /(\d+)(-)/g;
//$0(母亲), $1(第一个孩子), $2(第二个孩子)
str = str.replace(re, function($0, $1, $2) {
console.log('0:'+$0);
console.log('1:'+$1);
console.log('2:'+$2);
return $1 + '.';//等价于return $0.substring(0, $0.length - 1) + '.';
});
alert(str);//2019.2.14
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
打印结果:
0:2019-
1:2019
2:-
0:2-
1:2
2:-
1
2
3
4
5
6
7
2
3
4
5
6
7
当match不加g的时候才可以获取到子项的集合。
let str = 'abc';
let re = /(a)(b)(c)/;
alert( str.match(re) ); //[abc,a,b,c]
let str = 'abc';
let re = /(a)(b)(c)/g;
alert( str.match(re) ); //abc
1
2
3
4
5
6
7
2
3
4
5
6
7
# 2.3.2 回溯引用
- \1 : 重复的第一个子项
- \2 : 重复的第二个子项
let str = 'abcb';
let re = /(a)(b)(c)\2/;
alert( re.test(str) );//true
1
2
3
2
3
- 正向/反向查找
// 正向查找
a(?=b) // a的后缀b
a(?!b) // a的后缀不是b
/lin(?=hui)/.test('linhui'); // true
/lin(?!hui)/.test('linjuan'); // true
// 反向查找(正向的中间加小于号)
// 存在兼容问题
// 兼容方法:字符串翻转 + 正向 + 字符串翻转
(?<=a)b // b的前缀是a
(?<!a)b // b的前缀不是a
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
回溯查找 | 正则 | 记忆方式 |
---|---|---|
引用 | \0,\1,\2 和 $0, $1, $2 | 转义+数字 |
非捕获组 | (?😃 | 引用表达式(()), 本身不被消费(?),引用(😃,可提高性能 |
# 2.4 量词{}
{x} // 出现x次
{min,max} // 出现min-max次
{min,} // 至少min次
{0,max} // 至多max次
1
2
3
4
2
3
4
# 2.5 字符类[]
- 能够匹配包含在中括号中的一系列字符中的任意一个,但是匹配的结果只能够是其中的一个而不是多个。
^
写在[]里面:排除-
:确定一个匹配的范围- 连字符也是有原则的:前后两个字符是有顺序的,如果使用相同的编码,后面的字符码位应大于或等于前面字符的码位
[0-9]//ture
[9-0]//false
let str = 'abdc';
let re = /a[bde]c/;
alert(re.test(str));//false
1
2
3
2
3
# 三、常见正则表达式
# 3.1 表单校验
let re = {
email: /^\w+@[a-z0-9]+(\.[a-z]+){1,3}$/,
// 最短6位,最长16位 {6,16}
// 可以包含小写大母 [a-z] 和大写字母 [A-Z]
// 可以包含数字 [0-9]
// 可以包含下划线 [ _ ] 和减号 [ - ]
password: /^[\w_-]{6,16}$/
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
正则 | 含义 |
---|---|
[a-zA-Z] | 英文字母 |
[\u4e00-\u9fa5] | 中文 |
^\s*|\s*$ | 行首或行尾空格 |
^\w+@[a-z0-9]+(\.[a-z]+){1,3}$ | |
[a-zA-z]+://[^\s]* | 网址 |
[1-9][0-9]{4,9} | QQ号 |
[1-9]\d{5} | 邮政编码 |
[1-9]\d{14}|[1-9]\d{17}|[1-9]\d{16}x | 身份证 |
Email:起始至少为一个字符(\w字母,数字或者下划线),然后匹配@,接着为任意个字母或者数字,.代表真正的点,.后面为至少一个的字符(a-z),同时这个(比如
.com
)整体为一个子项作为结束,可以出现1-3次。因为有的邮箱是xxxx.@qq.com xxxx.@163.com xxxx.@16.cn.net
。
# 四、小案例
# 4.1 敏感词的过滤
let str1 = '我过敏的事过了,词';
let re = /过敏|过|词/g;
let str2 = '';
str2 = str1.replace(re, '*'); //我*的事*了,*
1
2
3
4
5
2
3
4
5
# 4.2 获取class的方法
<body>
<ul>
<li class="box1">111</li>
<li>111</li>
<li class="box1box2">111</li>
<li>111</li>
<li class="box1 box2">111</li>
</ul>
</body>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
只有第一行变红
<script>
window.onload = function() {
let aLi = getByClass(document, 'box1');
for (let i = 0; i < aLi.length; i++) {
aLi[i].style.background = 'red';
}
function getByClass(oParent, sClass) {
let arr = [];
let aEle = oParent.getElementsByTagName('*');
for (let i = 0; i < aEle.length; i++) {
if (aEle[i].className == sClass) {
arr.push(aEle[i]);
}
}
return arr;
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
第一行和第五行都变红
当正则需要传参的时候,一定要用构造函数的写法
function getByClass(oParent,sClass){
let arr = [];
let aEle = oParent.getElementsByTagName('*');
let re = new RegExp('\\b'+sClass+'\\b');
for(let i=0;i<aEle.length;i++){
if( re.test(aEle[i].className) ){
arr.push( aEle[i] );
}
}
return arr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 4.3 找重复项最多的字符和个数
let str = 'ahdkfhjasjkhdfjalghlsak';
let arr = str.split('');
str = arr.sort().join(''); //aaaaddffghhhhjjjkkkllss
let value = [];
let index = 0;
let re = /(\w)\1+/g;
str.replace(re, function($0, $1) {
if (index < $0.length) {
index = $0.length;
value = $1.split('');
} else if (index == $0.length) {
value.push($1);
}
});
alert('最多的字符:' + value + ',重复的次数:' + index); //最多的字符:a,h,重复的次数:4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 4.4 去掉前后空格
let str = ' hello ';
alert( '('+trim(str)+')' );//(hello)
function trim(str){
let re = /^\s+|\s+$/g;
return str.replace(re,'');
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 4.5 匹配成对标签转驼峰匹配
let str = 'border-bottom-color';
let re = /-(\w)/g;
str.replace(re,($0,$1) => {
return $1.toUpperCase();
}); // "borderBottomColor"
1
2
3
4
5
2
3
4
5
# 4.6 验证密码密度
//密码强度 : 数字 小写字母 大写字母
let str = 'AAAaaa1111';
let re1 = /\d/g;
let re2 = /[a-z]/g;
let re3 = /[A-Z]/g;
if( re1.test(str) && re2.test(str) && re3.test(str) ){
alert('困难');
}
else if((re1.test(str) && re2.test(str)) || (re1.test(str) && re3.test(str)) || (re2.test(str) && re3.test(str)) ){
alert('普通');
}
else if(re1.test(str) || re2.test(str) || re3.test(str)){
alert('简单');
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 4.7 匹配用户名和限制位数
//匹配用户名 : 汉字/字母/数字 (5-10位)
//必含字母,可能含汉字数字
let str = 'asfasf';
let re = /[a-z]/g;
let re2 = /[\u4e00-\u9fa50-9]?/g;
if( re.test(str) && re2.test(str) && str.length >= 5 && str.length <= 10 ){
alert('正确');
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 4.8 数字千分位写法
- 带有小数点
num.toLocaleString()
1
- 不带小数点
num.toString().replace(/(\d)(?=(?:\d{3})+$)/g,'$1,')
1
- 封装
function numFormat(num) {
if (num.toString().indexOf('.') !== -1) {
return num.toLocaleString();
} else {
return num.toString().replace(/(\d)(?=(?:\d{3})+$)/g,'$1,');
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 4.9 格式化金钱
const ThousandNum = num => num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
const money = ThousandNum(19941112);
// money => "19,941,112"
1
2
3
2
3
# 4.10 手机号
// 首位为1,第二位为3、4、5、7、8,后九位为任意数字
let re = /^1[3|4|7|8]\d{9}$/
1
2
2
# 4.11 大于0的正数
要排除的情况有0,0123,非负数,0.0,0.1,0.1023。
第一种情况:首位是0,但第二位必须是小数点,小数点有可能是0。
/^0\.\d*|[1-9]+)$/
1
第二种情况:首位不是0,但最后一位不是小数点。
/^[1-9][\.\d]\d+$/
1
合并一下
/(^0\.\d{0,}[1-9]+$)|(^[1-9][\.\d]\d+$)/
1